package com.zhanglinwei.common;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

@Component
public class SpringContext {

    private static final AtomicBoolean INIT = new AtomicBoolean(false);

    /**
     * volatile 确保多线程环境下对该变量的读取和写入始终是最新的
     */
    private static volatile ApplicationContext applicationContext;


    public SpringContext(ApplicationContext applicationContext) {
        if (applicationContext != null) {
            if (INIT.compareAndSet(false, true)) {
                SpringContext.applicationContext = applicationContext;
            }
        }
    }


    public static Object getBeanByName(String beanName) {
        return useApplicationContextBeforeCheck(ctx -> ctx.getBean(beanName));
    }


    public static <T> T getBean(Class<T> cls) {
        return useApplicationContextBeforeCheck(ctx -> ctx.getBean(cls));
    }
    public static Object getBean(String name) {
        return useApplicationContextBeforeCheck(ctx -> ctx.getBean(name));
    }

    public static <T> Map<String, T> getBeansOfType(Class<T> cls) {
        return useApplicationContextBeforeCheck(ctx -> ctx.getBeansOfType(cls));
    }

    public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> cls) {
        return useApplicationContextBeforeCheck(ctx -> ctx.getBeansWithAnnotation(cls));
    }


    public static void publishEvent(Object event) {
        useApplicationContextBeforeCheck(ctx -> {
            ctx.publishEvent(event);
            return null;
        });
    }

    public static void publishEvent(ApplicationEvent event) {
        useApplicationContextBeforeCheck(ctx -> {
            ctx.publishEvent(event);
            return null;
        });
    }

    private static void checkInit() {
        if (!INIT.get()) {
            throw new RuntimeException("SpringContext has not been initialized yet, please initialize it before use.");
        }
    }

    private static <R> R useApplicationContextBeforeCheck(Function<ApplicationContext, R> function) {
        checkInit();
        return function.apply(SpringContext.applicationContext);
    }
}
